﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using GWSoft.Framework.Core.EfDbContext;
using GWSoft.Framework.Core.Filters;
using GWSoft.Framework.Core.Helpers;
using GWSoft.Framework.Core.IoC;
using GWSoft.Framework.Core.Options;
using log4net;
using log4net.Repository;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.PlatformAbstractions;
using Microsoft.IdentityModel.Tokens;
using Serilog;
using Serilog.Events;
using Swashbuckle.AspNetCore.Swagger;
using Swashbuckle.AspNetCore.SwaggerGen;

namespace GWSoft.Framework.WebApi
{
    /// <summary>
    /// 全局启动类
    /// </summary>
    public class Startup
    {
        /// <summary>
        /// 日志配置
        /// </summary>
        public static ILoggerRepository repository { get; set; }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="configuration"></param>
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
            //初始化log4net
            repository = LogManager.CreateRepository("NETCoreRepository");
            Log4NetHelper.SetConfig(repository, "log4net.config");
        }

        /// <summary>
        /// 获取配置项
        /// </summary>
        public IConfiguration Configuration { get; }

        /// <summary>
        /// 配置服务
        /// </summary>
        /// <param name="services"></param>
        /// <returns></returns>
        public IServiceProvider ConfigureServices(IServiceCollection services)
        {
            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                .AddJwtBearer(options =>
                {
                    var serverSecret = new SymmetricSecurityKey(
                        Encoding.UTF8.GetBytes(Configuration["JWT:ServerSecret"]));

                    options.TokenValidationParameters = new TokenValidationParameters
                    {
                        IssuerSigningKey = serverSecret,
                        ValidIssuer = Configuration["JWT:Issue"],
                        ValidAudience = Configuration["JWT:Audience"]
                    };
                });
            services.AddMvc(option =>
            {
                option.Filters.Add(new GlobalExceptionFilter());
            });
            services.AddMemoryCache();//启用MemoryCache
            services.AddDistributedRedisCache(option =>
            {
                option.Configuration = "10.21.3.249";//redis连接字符串
                option.InstanceName = "";//Redis实例名称
            });//启用Redis

            services.Configure<MemoryCacheEntryOptions>(
                    options => options.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5)) //设置MemoryCache缓存有效时间为5分钟。
                .Configure<DistributedCacheEntryOptions>(option =>
                    option.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5));//设置Redis缓存有效时间为5分钟。

            services.AddSwaggerGen(c => 
            {
                c.SwaggerDoc("v1", new Swashbuckle.AspNetCore.Swagger.Info
                {
                    Version = "v1",
                    Title = "观为智慧RESTful API 接口文档",
                    Description = "系统管理API接口",
                });
                // Swagger 2.+ support
                var security = new Dictionary<string, IEnumerable<string>>
                {
                    {"Bearer", new string[] { }},
                };

                c.AddSecurityDefinition("Bearer", new ApiKeyScheme
                {
                    Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"",
                    Name = "Authorization",
                    In = "header",
                    Type = "apiKey"
                });
                c.AddSecurityRequirement(security);

                var basePath = PlatformServices.Default.Application.ApplicationBasePath;
                var xmlPath = Path.Combine(basePath, "GWSoft.Framework.WebApi.xml");
                var xmlControllerPath = Path.Combine(basePath, "GWSoft.Framework.WebSite.Controllers.xml");
                var xmlModelsPath = Path.Combine(basePath, "GWSoft.Framework.WebSite.Models.xml");
                c.IncludeXmlComments(xmlPath);
                c.IncludeXmlComments(xmlControllerPath);
                c.IncludeXmlComments(xmlModelsPath);
            });
            
            return InitIoC(services);
        }

        /// <summary>
        /// 全局配置项
        /// </summary>
        /// <param name="app"></param>
        /// <param name="env"></param>
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            app.UseAuthentication();

            Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseBrowserLink();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }

            app.UseSwagger();
            app.UseSwaggerUI(c =>
            {
                c.SwaggerEndpoint("../swagger/v1/swagger.json", "GWSoft Framework API V1");
            });

            app.UseStaticFiles();
            
            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
        }

        /// <summary>
        /// IoC初始化
        /// </summary>
        /// <param name="services"></param>
        /// <returns></returns>
        private IServiceProvider InitIoC(IServiceCollection services)
        {
            var connectionString = Configuration.GetConnectionString("MsSqlServer");
            var dbContextOption = new DbContextOption
            {
                ConnectionString = connectionString,
                ModelAssemblyName = "GWSoft.Framework.WebSite.Models",
                DbType = DbType.MSSQLSERVER
            };
            var codeGenerateOption = new CodeGenerateOption
            {
                ModelsNamespace = "GWSoft.Framework.WebSite.Models",
                IRepositoriesNamespace = "GWSoft.Framework.WebSite.IRepositories",
                RepositoriesNamespace = "GWSoft.Framework.WebSite.Repositories",
                IServicsNamespace = "GWSoft.Framework.WebSite.IServices",
                ServicesNamespace = "GWSoft.Framework.WebSite.Services"
            };

            IoCContainer.Register(Configuration);//注册配置
            IoCContainer.Register(dbContextOption);//注册数据库配置信息
            IoCContainer.Register(codeGenerateOption);//注册代码生成器相关配置信息
            IoCContainer.Register(typeof(DefaultDbContext));//注册EF上下文
            IoCContainer.Register("GWSoft.Framework.WebSite.Repositories", "GWSoft.Framework.WebSite.IRepositories");//注册仓储
            IoCContainer.Register("GWSoft.Framework.WebSite.Services", "GWSoft.Framework.WebSite.IServices");//注册service
            return IoCContainer.Build(services);
        }

        /// <summary>
        /// 验证过滤方法
        /// </summary>
        public class AuthorizationHeaderParameterOperationFilter : IOperationFilter
        {
            /// <summary>
            /// Apply
            /// </summary>
            /// <param name="operation"></param>
            /// <param name="context"></param>
            public void Apply(Operation operation, OperationFilterContext context)
            {
                var filterPipeline = context.ApiDescription.ActionDescriptor.FilterDescriptors;
                var isAuthorized = filterPipeline.Select(filterInfo => filterInfo.Filter).Any(filter => filter is AuthorizeFilter);
                var allowAnonymous = filterPipeline.Select(filterInfo => filterInfo.Filter).Any(filter => filter is IAllowAnonymousFilter);

                if (isAuthorized && !allowAnonymous)
                {
                    if (operation.Parameters == null)
                        operation.Parameters = new List<IParameter>();

                    operation.Parameters.Add(new NonBodyParameter
                    {
                        Name = "Authorization",
                        In = "header",
                        Description = "access token",
                        Required = true,
                        Type = "string"
                    });
                }
            }
        }
    }
}
